package com.express.gateway.manage.handler;

import com.express.gateway.common.CommonCons;
import com.express.gateway.common.EventBusTopicConst;
import com.express.gateway.common.ResultFormat;
import com.express.gateway.common.util.DateTimeUtils;
import com.express.gateway.common.util.StrUtil;
import com.express.gateway.manage.auth.RolesConstant;
import com.express.gateway.manage.auth.UserAuthUtil;
import com.express.gateway.manage.enums.ContentTypeEnum;
import com.express.gateway.manage.enums.StatusCodeEnum;
import com.express.gateway.manage.options.AppConfig;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.eventbus.ReplyException;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.web.RoutingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.time.Instant;
import java.util.Objects;
import java.util.Optional;

import static com.express.gateway.common.CommonCons.KEY_CLUSTER;
import static com.express.gateway.common.CommonCons.NOT_FONT_HTML;

public class AppConstructHandler {
    private static final Logger LOG = LogManager.getLogger(AppConstructHandler.class);

    private final String vertxName;

    private final Vertx vertx;

    public AppConstructHandler(Vertx vertx, String vertxName) {
        this.vertxName = vertxName;
        this.vertx = vertx;
    }
    /**
     * 查看所有应用程序
     *
     * @param rct
     */
    public void appListTemp(RoutingContext rct) {
        LOG.info("[user : {}] 执行查询应用", rct.session().<String>get(CommonCons.KEY_USER_NAME));
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        // 查询应用是否已经启动
        Promise<JsonArray> onlineFuture = Promise.promise();
        onlineFuture.future().onFailure(ex -> {
            LOG.error("[user : {}] 执行查询应用 失败:{}", ex.getMessage(), rct.session().<String>get(CommonCons.KEY_USER_NAME));
            response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
        }).onSuccess(body -> {
            vertx.eventBus().<JsonArray>request(EventBusTopicConst.DEPLOY_FIND_ONLINE_APP, null).onFailure(ex -> {
                rct.reroute("/app/app_list.ftl");
                LOG.error("query online app is error:{}", ex.getMessage());
            }).onSuccess(rs -> {
                JsonArray array = new JsonArray();
                JsonArray online = rs.body();
                body.forEach(obj -> {
                    JsonObject data = (JsonObject) obj;
                    String appName = data.getString("appName");
                    data.put("appName", appName).put("describe", data.getString("describe"));
                    data.put("time", DateTimeUtils.DATE_TIME.formatDate(data.getInstant("time")));
                    data.put("scope", data.getInteger("scope")).put("online", online.contains(appName));
                    array.add(data);
                });
                rct.reroute("/app/app_list.ftl");
            });
        });

        // 查询所有APP
        vertx.eventBus().<JsonArray>request(EventBusTopicConst.FIND_APP, null).onFailure(ex -> {
            LOG.error("[user : {}] 执行查询应用 失败:{}", ex.getMessage(), rct.session().<String>get(CommonCons.KEY_USER_NAME));
            response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
        }).onSuccess(rs -> {
            JsonArray body = rs.body();
            if (body.isEmpty()) {
                response.end(ResultFormat.format(StatusCodeEnum.C200, body));
                return;
            }
            // body.put("online", rs.body());
            JsonObject context = new JsonObject();
            context.put(CommonCons.KEY_APP, body);
            rct.put(CommonCons.KEY_CONTEXT, context);
            // 通知完成
            onlineFuture.complete(rs.body());
            // findOnlineApps(rct, body);
        });;
    }

    /**
     * 查看一个应用程序
     *
     * @param rct
     */
    public void appDetailTemp(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_NAME);
        if (StrUtil.isNullOrEmpty(appName)) {
            rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val()).end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.HTML_UTF8.val());
        // 查看应用是否已经启动
        Promise<JsonObject> onlineFuture = Promise.promise();
        onlineFuture.future().onComplete(res -> {
            vertx.eventBus().<Boolean>request(EventBusTopicConst.DEPLOY_APP_IS_ONLINE, appName).onFailure(ex -> {
                response.end(ex.getMessage());
            }).onSuccess( rs -> {
                JsonObject body = new JsonObject(res.result().getString(CommonCons.KEY_CONTENT));
                body.put("online", rs.body());
                JsonObject context = new JsonObject();
                context.put(CommonCons.KEY_APP, body);
                rct.put(CommonCons.KEY_CONTEXT, context);
                rct.reroute("/app/app_detail.ftl");
            });
        });

        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        LOG.info("[user : {}] 执行查看应用 {}", userName, appName);
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.GET_APP, appName).onFailure(ex -> {
            response.end(ex.getMessage());
        }).onSuccess(rs -> {
            JsonObject body = rs.body();
            if (body == null || body.isEmpty()) {
                response.setStatusCode(404).end(NOT_FONT_HTML);
                return;
            }
            // 通知已完成
            onlineFuture.complete(body);
        });
    }

    /**
     * 渲染一个将要修改的应用程序
     *
     * @param rct
     */
    public void appUpdateTemp(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_NAME);
        if (StrUtil.isNullOrEmpty(appName)) {
            rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val()).end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.HTML_UTF8.val());
        LOG.info("[user : {}] 执行查看应用 {}", userName, appName);
        vertx.eventBus().<JsonObject>request(EventBusTopicConst.GET_APP, appName).onFailure(ex -> {
            response.end(ex.getMessage());
        }).onSuccess(rs -> {
            JsonObject body = rs.body();
            JsonObject app = new JsonObject(body.getString("content"));
            if (app.isEmpty()) {
                response.setStatusCode(404).end(NOT_FONT_HTML);
                return;
            }
            JsonObject context = new JsonObject().put("app", app);
            rct.put(CommonCons.KEY_CONTEXT, context);
            rct.reroute("/app/app_update.ftl");
        });
    }

    /**
     * 渲染一个将要修改的应用程序
     *
     * @param rct
     */
    public void appCreateTemp(RoutingContext rct) {
        rct.put(CommonCons.KEY_CONTEXT, null);
        rct.reroute("/app/app_update.ftl");
    }

    /**
     * 添加应用
     *
     * @param rct
     */
    public void addAppAction(RoutingContext rct) {
        User user = rct.user();
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        UserAuthUtil.isAuthorized(user, RolesConstant.WRITE, res -> {
            if (res.failed()) {
                LOG.error("[user : {}] 执行添加应用 失败:{}", userName, res.cause());
                response.end(ResultFormat.format(StatusCodeEnum.C500, res.cause().getMessage()));
                return;
            }
            if (!res.result()) {
                LOG.error("[user : {}] 执行添加应用 失败:未授权或者无权利", Optional.ofNullable(userName));
                response.end(ResultFormat.formatAsZero(StatusCodeEnum.C401));
                return;
            }
            LOG.info("[user : {}] 执行添加应用...", userName);
            AppConfig config = AppConfig.fromJson(rct.body().asJsonObject());
            if (Objects.isNull(config)) {
                LOG.error("[user : {}] 执行添加应用 失败: Body json is null", userName);
                return;
            }
            JsonObject param = new JsonObject();
            param.put(CommonCons.KEY_APP_NAME, config.getAppName());
            param.put(CommonCons.KEY_APP, config.toJson().put(CommonCons.KEY_TIME, Instant.now()));
            vertx.eventBus().<Integer>request(EventBusTopicConst.ADD_APP, param).onFailure(ex -> {
                LOG.error("[user : {}] 执行添加应用 失败:{}", userName, ex.getMessage());
                StatusCodeEnum code = ex.getCause().toString().contains("UNIQUE") ? StatusCodeEnum.C1406 : StatusCodeEnum.C500;
                response.end(ResultFormat.format(code, ex.getMessage()));
            }).onSuccess(asr -> {
                response.end(ResultFormat.format(StatusCodeEnum.C200, asr.body()));
                LOG.info("[user : {}] 执行添加应用 结果: {}", userName, asr.body());
            });
        });
    }


    /**
     * 修改一个应用
     *
     * @param rct
     */
    public void updateAppAction(RoutingContext rct) {
        User user = rct.user();
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        UserAuthUtil.isAuthorized(user, RolesConstant.WRITE, res -> {
            if (res.failed()) {
                LOG.error("[user : {}] 执行修改应用 失败:{}", userName, res.cause());
                response.end(ResultFormat.format(StatusCodeEnum.C500, res.cause().getMessage()));
                return;
            }
            if (!res.result()) {
                LOG.error("[user : {}] 执行修改应用 失败:未授权或者无权利", Optional.ofNullable(userName));
                response.end(ResultFormat.formatAsZero(StatusCodeEnum.C401));
                return;
            }
            LOG.info("[user : {}] 执行修改应用...", rct.session().<String>get(CommonCons.KEY_USER_NAME));
            // rct.getBodyAsJson()
            AppConfig dto = AppConfig.fromJson(rct.body().asJsonObject());
            if (Objects.isNull(dto)) {
                LOG.error("[user : {}] 执行修改应用 失败: Body json is null", userName);
                return;
            }
            JsonObject param = new JsonObject();
            param.put(CommonCons.KEY_APP_NAME, dto.getAppName());
            param.put(CommonCons.KEY_APP, dto.toJson());
            vertx.eventBus().<Integer>request(EventBusTopicConst.UPD_APP, param).onFailure(ex -> {
                LOG.error("[user : {}] 执行修改应用 失败:{}", userName, ex.getMessage());
                response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
            }).onSuccess(ars -> {
                response.end(ResultFormat.format(StatusCodeEnum.C200, ars.body()));
                String sessionContent = rct.session().get(CommonCons.KEY_USER_NAME);
                LOG.info("[user : {}] 执行修改应用:{} 结果: {}", sessionContent, ars.body(), dto.getAppName());
            });
        });
    }

    /**
     * 删除应用程序
     *
     * @param rct
     */
    public void delAppAction(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_NAME);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        if (StrUtil.isNullOrEmpty(appName)) {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }
        User user = rct.user();
        String userName = rct.session().get(CommonCons.KEY_USER_NAME);
        UserAuthUtil.isAuthorized(user, RolesConstant.WRITE, res -> {
            if (res.failed()) {
                LOG.error("[user : {}] 执行删除应用:{} 失败:{}", userName, res.cause(), appName);
                response.end(ResultFormat.format(StatusCodeEnum.C500, res.cause().getMessage()));
                return;
            }
            JsonObject config = new JsonObject().put(CommonCons.KEY_APP_NAME, appName);
            vertx.eventBus().request(EventBusTopicConst.DEPLOY_APP_UNDEPLOY, config);
            if (!res.result()) {
                LOG.error("[user : {}] 执行删除应用:{} 失败:未授权或者无权限", userName, appName);
                response.end(ResultFormat.formatAsZero(StatusCodeEnum.C401));
                return;
            }
            LOG.info("[user : {}] 执行删除应用{}...", rct.session().<String>get(CommonCons.KEY_USER_NAME), appName);
            vertx.eventBus().<Integer>request(EventBusTopicConst.DEL_APP, appName).onFailure(ex -> {
                LOG.error("[user : {}] 执行删除应用:{} 失败:{}", userName, appName, ex.getMessage());
                response.end(ResultFormat.format(StatusCodeEnum.C500, ex.getMessage()));
            }).onSuccess(asr -> {
                response.end(ResultFormat.format(StatusCodeEnum.C200, asr.body()));
                LOG.info("[user : {}] 执行删除应用:{} 结果: {}", rct.session().<String>get(CommonCons.KEY_USER_NAME), asr.body(), appName);
            });
            // 将应用暂停
            if (!vertx.isClustered()) {
                return;
            }
            String cloudAdds = KEY_CLUSTER + EventBusTopicConst.DEPLOY_APP_UNDEPLOY;
            vertx.eventBus().publish(cloudAdds, config.copy().put(CommonCons.KEY_VERTX, vertxName));
            LOG.info("执行删除应用 广播告诉集群环境中暂停应用:" + appName);
        });
    }

    /**
     * 启动应用
     *
     * @param rct
     */
    public void deployAppAction(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_NAME);
        LOG.info("执行启动应用:{}", appName);
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        if (StrUtil.isNullOrEmpty(appName)) {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }

        Promise<JsonObject> deployAppFuture = Promise.promise();
        deployAppFuture.future().onSuccess(conf -> {
            vertx.eventBus().<String>request(EventBusTopicConst.DEPLOY_APP_DEPLOY, conf).onFailure( ex -> {
                LOG.error("启动应用:{} ,失败:{}", appName, ex.getMessage());
                StatusCodeEnum msgCode = StatusCodeEnum.C500;
                if (ex.getCause() instanceof ReplyException && ((ReplyException) ex.getCause()).failureCode() == 1111) {
                    msgCode = StatusCodeEnum.C1111;
                }
                response.end(ResultFormat.formatAsZero(msgCode));
            }).onSuccess(rs -> {
                LOG.info("启动应用 " + appName + ":成功!");
                response.end(ResultFormat.formatAsOne(StatusCodeEnum.C200));
                if (!vertx.isClustered()) {
                    return;
                }
                String cluAdds = KEY_CLUSTER + EventBusTopicConst.DEPLOY_APP_DEPLOY;
                vertx.eventBus().publish(cluAdds, conf.put(CommonCons.KEY_VERTX, vertxName));
                LOG.info("广播告诉集群环境中启动应用:" + appName);
            });
        });

        vertx.eventBus().<JsonObject>request(EventBusTopicConst.GET_APP, appName).onFailure(ex -> {
            LOG.error("启动应用:{} ,失败:{}", appName, ex.getMessage());
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C500));
        }).onSuccess(body -> {
            if (body.body().isEmpty()) {
                response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
                return;
            }
            JsonObject app = new JsonObject(body.body().getString(CommonCons.KEY_CONTENT));
            JsonObject config = new JsonObject();
            config.put(CommonCons.KEY_APP, app);
            config.put(CommonCons.KEY_APP_NAME, appName);
            // 通知已经完成
            deployAppFuture.complete(config);
        });
    }

    /**
     * 卸载应用
     *
     * @param rct
     */
    public void unDeployAppAction(RoutingContext rct) {
        String appName = rct.request().getParam(CommonCons.KEY_NAME);
        LOG.info("执行暂停应用 " + appName + "...");
        HttpServerResponse response = rct.response().putHeader(CommonCons.CONTENT_TYPE, ContentTypeEnum.JSON_UTF8.val());
        if (StrUtil.isNullOrEmpty(appName)) {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C1404));
            return;
        }
        JsonObject config = new JsonObject().put(CommonCons.KEY_APP_NAME, appName);
        vertx.eventBus().<String>request(EventBusTopicConst.DEPLOY_APP_UNDEPLOY, config).onFailure(ex -> {
            response.end(ResultFormat.formatAsZero(StatusCodeEnum.C500));
            LOG.error("执行暂停应用 " + appName + " 失败:" + ex.getMessage());
        }).onSuccess(rs -> {
            LOG.info("执行暂停应用 " + appName + " 成功!");
            response.end(ResultFormat.formatAsOne(StatusCodeEnum.C200));
            if (!vertx.isClustered()) {
                return;
            }
            String cluAdds = KEY_CLUSTER + EventBusTopicConst.DEPLOY_APP_UNDEPLOY;
            vertx.eventBus().publish(cluAdds, config.copy().put(CommonCons.KEY_VERTX, vertxName));
            LOG.info("广播集群环境中启动应用:" + appName);
        });
    }

}
